﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Kokomo.PeInspector.Reflection.Disassembly;

using AssemblyDependencyIndex = System.Collections.Generic.Dictionary<Kokomo.Mirror.CliMetadata.ModuleMemberReference, System.Linq.IGrouping<Kokomo.Mirror.CliMetadata.ModuleMemberReference, Kokomo.PeInspector.Reflection.Disassembly.MsilDependency>>;
using Kokomo.Mirror.CliMetadata;

namespace Kokomo.PeInspector.Reflection
{
	class ResidentDependencyIndex : DependencyIndex
	{
		internal ResidentDependencyIndex(IAssemblyListHost host)
			: base(host)
		{
			this._assemblyDependencyIndexTable = new Dictionary<Assembly, AssemblyDependencyIndex>();
		}

		protected override void AddDependenciesToIndex(Assembly assembly)
		{
			AssemblyDependencyIndex dependencyGroupings = this.CalculateDependencies(assembly).ToDictionary(g => g.Key);

			lock (this._assemblyDependencyIndexTable)
			{
				AssemblyDependencyIndex dependencyIndex = dependencyGroupings;
				this._assemblyDependencyIndexTable.Add(assembly, dependencyIndex);
			}

			//foreach (var entry in dependencyGroupings)
			//{
			//    this._dependencyLookup.Add(entry.Key, entry);
			//}
			//IDictionary<ModuleMemberReference, IGrouping<ModuleMemberReference, MsilDependency>> dependencyIndex = dependencyGroupings.ToDictionary(g => g.Key);
			//this._dependencyLookup = dependencyIndex;
		}

		private Dictionary<Assembly, AssemblyDependencyIndex> _assemblyDependencyIndexTable;

		public override IEnumerable<MsilDependency> GetDependenciesOf(
			ModuleMemberReference dependentMemberRef,
			ProgressInfo progress
			)
		{
			if (ModuleMemberReference.IsEmpty(dependentMemberRef)) throw new ArgumentNullException("dependentMember");

			IEnumerable<MsilDependency> dependencies = null;
			MemberInfo member = dependentMemberRef.LoadMember();
			if (this.WaitForIndex(TimeSpan.Zero))
			{
				lock (this._assemblyDependencyIndexTable)
				{
					AssemblyDependencyIndex index;
					if (this._assemblyDependencyIndexTable.TryGetValue(member.Module.Assembly, out index))
					{
						IGrouping<ModuleMemberReference, MsilDependency> assemblyDependencies;
						if (index.TryGetValue(dependentMemberRef, out assemblyDependencies))
						{
							dependencies = assemblyDependencies;
						}
					}
				}
			}
			else
			{
				dependencies = this.CalculateMemberDependencies(member);
			}

			return dependencies ?? new MsilDependency[0];
		}

		private Assembly[] DetermineAssembliesToSearch(ModuleMemberReference dependentMember)
		{
			return null;
		}

		public override IEnumerable<IGrouping<ModuleMemberReference, MsilMemberDependency>> GetDependentsOf(
			ModuleMemberReference dependencyMember,
			ProgressInfo progress
			)
		{
			if (ModuleMemberReference.IsEmpty(dependencyMember)) throw new ArgumentNullException("dependencyMember");

			Assembly[] assembliesToSearch = this.DetermineAssembliesToSearch(dependencyMember);

			this.WaitForIndex();

			List<IGrouping<ModuleMemberReference, MsilMemberDependency>> dependents = new List<IGrouping<ModuleMemberReference, MsilMemberDependency>>();

			lock (this._assemblyDependencyIndexTable)
			{
				if (progress != null)
				{
					progress.Maximum = this._assemblyDependencyIndexTable.Count;
					progress.Progress = 0;
				}

				foreach (var assemblyIndex in this._assemblyDependencyIndexTable.Values)
				{
					if (progress != null)
						progress.Progress++;

					foreach (var item in assemblyIndex)
					{
						MsilMemberDependency[] dependencies = item.Value.OfType<MsilMemberDependency>().Where(d => d.Matches(dependencyMember)).ToArray();
						if (dependencies.Length > 0)
							dependents.Add(new Grouping<ModuleMemberReference, MsilMemberDependency>(item.Key, dependencies));
					}
				}
			}

			return dependents;
		}
	}
}
